package Acme.Serve;

import java.util.Calendar;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Vector;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionContext;

import org.apache.tomcat.modules.session.SessionIdGenerator;

public class ServeSessionManager {

	// Max Inactive Interval is 30 minutes.
	public static final int MAX_INACTIVE_INTERVAL = 1800;
	public static final String SESSION_COOKIE_NAME = "sessid";
	private static ServeSessionManager instance = null;
	private Map<String, HttpSession> sessions = new HashMap<String, HttpSession>();

	public static ServeSessionManager getInstance() {
		if (instance == null)
			instance = new ServeSessionManager();
		return instance;
	}

	protected HttpSession getSession(String sessionId) {
		return sessions.get(sessionId);
	}

	protected HttpSession createSession() {
		SessionIdGenerator gen = new SessionIdGenerator();
		HttpSession session = new ServeHttpSession(gen.getIdentifier(null));
		this.sessions.put(session.getId(), session);
		return session;
	}

	public HttpSession getSession(String urlSessionStr,
			Map<String, Cookie> cookies) {
		HttpSession session = null;
		if (urlSessionStr != null
				&& urlSessionStr.startsWith(SESSION_COOKIE_NAME)) {
			String sessionId = urlSessionStr.substring(
					urlSessionStr.indexOf('=') + 1).trim();
			if (sessionId.length() != 0) {
				session = getSession(sessionId);
			}
		}

		// If session is not found from the URL string
		if (session == null && cookies != null && !cookies.isEmpty()) {
			session = getSessionFromCookie(cookies);
		}
		// Check if the session is still active.
		if (session != null) {
			ServeHttpSession serveSession = (ServeHttpSession) session;
			serveSession.lastAccessedTime = Calendar.getInstance()
					.getTimeInMillis();
			if (serveSession.lastAccessedTime - serveSession.creationTime > serveSession.maxInactiveInterval * 1000) {
				session.invalidate();
				session = null;
			}
		}
		// In the end, if we don't have a session (for example, if
		// its invalidated), create a new one.
		if (session == null) {
			session = createSession();
		}
		return session;
	}

	public HttpSession getSessionFromCookie(Map<String, Cookie> cookies) {
		Cookie sessionCookie = cookies.get(SESSION_COOKIE_NAME);
		HttpSession session = null;
		if (sessionCookie != null) {
			session = getSession(sessionCookie.getValue());
		}
		return session;
	}

	private class ServeHttpSession implements HttpSession {

		private Map<String, Object> attributes;
		private long creationTime;
		private String sessionId;
		private long lastAccessedTime;
		private int maxInactiveInterval;

		public ServeHttpSession(String sessionId) {
			this.sessionId = sessionId;
			this.creationTime = Calendar.getInstance().getTimeInMillis();
			this.lastAccessedTime = this.creationTime;
			this.maxInactiveInterval = ServeSessionManager.MAX_INACTIVE_INTERVAL;
			this.attributes = new HashMap<String, Object>();
		}

		public Object getAttribute(String attributeId) {
			return this.attributes.get(attributeId);
		}

		public Enumeration<String> getAttributeNames() {
			return (new Vector<String>(this.attributes.keySet())).elements();
		}

		public long getCreationTime() {
			return this.creationTime;
		}

		public String getId() {
			return this.sessionId;
		}

		public long getLastAccessedTime() {
			return lastAccessedTime;
		}

		public int getMaxInactiveInterval() {
			return this.maxInactiveInterval;
		}

		public ServletContext getServletContext() {
			// TODO Return the instance of Serve.
			return null;
		}

		public Object getValue(String arg0) {
			return getAttribute(arg0);
		}

		public String[] getValueNames() {
			String[] out = new String[this.attributes.keySet().size()];
			return this.attributes.keySet().toArray(out);
		}

		public void invalidate() {
			this.attributes.clear();
			// Remove this session from the list of sessions.
			sessions.remove(this.sessionId);
		}

		public boolean isNew() {
			return this.creationTime == this.lastAccessedTime;
		}

		public void putValue(String arg0, Object arg1) {
			this.setAttribute(arg0, arg1);
		}

		public void removeAttribute(String id) {
			this.attributes.remove(id);
		}

		public void removeValue(String arg0) {
			this.removeAttribute(arg0);
		}

		public void setAttribute(String id, Object value) {
			this.attributes.put(id, value);
		}

		public void setMaxInactiveInterval(int arg0) {
			this.maxInactiveInterval = arg0;
		}

		public HttpSessionContext getSessionContext() {
			// TODO Auto-generated method stub
			return null;
		}
	}
}
